﻿#region Namespace References

using System;
using System.Collections.Generic;
using DynConDevCorp.OpenArch.SmartData.Interfaces;
using DynConDevCorp.OpenArch.SmartData.Interfaces.Attributes;
using DynConDevCorp.OpenArch.SmartData.Interfaces.EventArgClasses;
using DynConDevCorp.OpenArch.SmartData.Interfaces.MetaData;

#endregion

namespace DynConDevCorp.OpenArch.SmartData.Core
{
    public abstract class SmartEntityLink : ISmartEntityLink
    {

        protected void WireEvents()
        {
            RawLinkedEntity.FieldValueChanged += RawLinkedEntity_FieldValueChanged;
            RawLinkedEntity.RawValueChanged += RawLinkedEntity_RawValueChanged;
        }

        private void RawLinkedEntity_RawValueChanged(object sender, ValueChangedEventArgs<object> e)
        {
            if (RawValueChanged != null)
                RawValueChanged(this, e);
        }

        private void RawLinkedEntity_FieldValueChanged(object sender, FieldValueChangedEventArgs e)
        {
            if (FieldValueChanged != null)
                FieldValueChanged(this, e);
        }

        protected void UnWireEvents()
        {
            RawLinkedEntity.RawValueChanged -= RawLinkedEntity_RawValueChanged;
            RawLinkedEntity.FieldValueChanged -= RawLinkedEntity_FieldValueChanged;
        }

        public Guid LinkedEntityID { get { return RawLinkedEntity.EntityID; } }
        public abstract void SetRawLinkedEntity(ISmartEntity value);

        #region ISmartEntityLink Members

        /// <summary>
        /// Gets a value indicating whether this instance is dynamic entity.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is dynamic entity; otherwise, <c>false</c>.
        /// </value>
        public virtual Boolean IsDynamicEntity { get { throw new NotImplementedException(); } }
        
        /// <summary>
        /// Adds the field effect.
        /// </summary>
        /// <typeparam name="TData">The type of the data.</typeparam>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="effect">The effect.</param>
        public void AddFieldEffect<TData>(string fieldName, ISmartEffect<TData> effect)
        {
            ISmartEffectCollection fieldEffectCollection;
            if (!m_FieldEffects.TryGetValue(fieldName, out fieldEffectCollection))
            {
                fieldEffectCollection = new SmartEffectCollection();
                m_FieldEffects.Add(fieldName, fieldEffectCollection);
            }
            fieldEffectCollection.Add(effect);
        }

        public void AddFieldEffect<TData>(string fieldName, EventHandler<ValueChangedEventArgs<TData>> eventHandler)
        {
            SmartEffect<TData> newEffect = new SmartEffect<TData>(eventHandler);
            AddFieldEffect(fieldName, newEffect);
        }

        public void Commit()
        {
            RawLinkedEntity.Commit();
        }

        public bool ContainsField(string fieldName)
        {
            return RawLinkedEntity.ContainsField(fieldName);
        }


        public event EventHandler<DisplayValueChangedEventArgs> DisplayValueChanged;

        public void EnableTrackChanges(bool state)
        {
            RawLinkedEntity.EnableTrackChanges(state);
        }

        public event EventHandler<FieldValueChangedEventArgs> FieldValueChanged;

        public string FormattedDisplay(string format)
        {
            return RawLinkedEntity.FormattedDisplay(format);
        }

        public string GetFieldName(ISmartField field)
        {
            return RawLinkedEntity.GetFieldName(field);
        }

        public void MarkAsDirty()
        {
            RawLinkedEntity.MarkAsDirty();
        }

        public void PropogateFieldEffects<TData>(ISmartField source, ValueChangedEventArgs<TData> args)
        {
            ISmartEffectCollection fieldEffectCollection;

            if (m_FieldEffects.TryGetValue(args.FieldName, out fieldEffectCollection))
            {
                foreach (ISmartEffect rawFieldEffect in fieldEffectCollection)
                {
                    ISmartEffect<TData> typedFieldEffect = rawFieldEffect as ISmartEffect<TData>;
                    if (typedFieldEffect != null)
                    {
                        typedFieldEffect.InvokeEffect(this, args);
                    }
                }
            }

            if (OwningEntity != null)
            {
                OwningEntity.PropogateFieldEffects(this, args);
            }
        }

        public event EventHandler<ValueChangedEventArgs<object>> RawValueChanged;

        public void RegisterAutoFields()
        {
            throw new NotSupportedException();
        }

        public ISmartField RegisterField(string fieldName, ISmartField field)
        {
            throw new NotSupportedException();
        }

        public void RegisterFieldEffectForType<TData>(string fieldName, ISmartEffect<TData> effect)
        {
            throw new NotImplementedException();
        }

        public void RegisterFieldEffectForType<TData>(string fieldName,
                                                      EventHandler<ValueChangedEventArgs<TData>> eventHandler)
        {
            SmartEffect<TData> newEffect = new SmartEffect<TData>(eventHandler);
            RegisterFieldEffectForType(fieldName, newEffect);
        }

        /// <summary>
        /// Removes the field effect.
        /// </summary>
        /// <typeparam name="TData">The type of the data.</typeparam>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="effect">The effect.</param>
        public void RemoveFieldEffect<TData>(string fieldName, ISmartEffect<TData> effect)
        {
            ISmartEffectCollection fieldEffectCollection;
            if (m_FieldEffects.TryGetValue(fieldName, out fieldEffectCollection))
            {
                fieldEffectCollection.Remove(effect);
                if (fieldEffectCollection.Count == 0)
                    m_FieldEffects.Remove(fieldName);
            }
        }

        public void Rollback()
        {
            RawLinkedEntity.Rollback();
        }

        public void SetOwningEntity(ISmartEntity entity)
        {
            m_OwningEntity = entity;
        }

        public void SetRawFieldValue(string fieldName, object value)
        {
            throw new NotSupportedException();
        }

        #endregion

        #region Public Instance Properties

        public Type DataType
        {
            get { return RawLinkedEntity.DataType; }
        }

        public string DisplayFormatString { get; set; }

        public string DisplayValue
        {
            get { return RawLinkedEntity.DisplayValue; }
        }

        public Guid EntityID
        {
            get { return RawLinkedEntity.EntityID; }
        }

        public int FieldCount
        {
            get { return RawLinkedEntity.FieldCount; }
        }

        public IList<string> FieldNames
        {
            get { return RawLinkedEntity.FieldNames; }
        }

        public ISmartFieldKeyedCollection<string, ISmartField> Fields
        {
            get { return RawLinkedEntity.Fields; }
        }

        public PartialBoolean InputInhibited
        {
            get {
                return RawLinkedEntity != null ? RawLinkedEntity.InputInhibited : m_InputInhibited;
            }
            set
            {
                m_InputInhibited = value;
                if (RawLinkedEntity != null)
                    RawLinkedEntity.InputInhibited = value;
            }
        }
        private PartialBoolean m_InputInhibited;

        public string InputValue
        {
            set { RawLinkedEntity.InputValue = value; }
        }

        public PartialBoolean IsDirty
        {
            get { return RawLinkedEntity.IsDirty; }
        }

        [Persist(IsXmlPersistable = false, IsDatabaseField = false)]
        public ISmartField this[string fieldName]
        {
            get { return RawLinkedEntity[fieldName]; }
        }

        public ICollection<ISmartEntity> LinkedEntities
        {
            get { throw new NotImplementedException(); }
        }

        public ISmartEntity OwningEntity
        {
            get { return m_OwningEntity; }
        }

        public SmartPersistenceState PersistenceState
        {
            get { return RawLinkedEntity.PersistenceState; }
            set { RawLinkedEntity.PersistenceState = value; }
        }

        public object RawValue
        {
            get { return RawLinkedEntity.RawValue; }
            set { RawLinkedEntity.RawValue = value; }
        }

        public PartialBoolean TrackingChanges
        {
            get { return RawLinkedEntity.TrackingChanges; }
        }

       public PartialBoolean WriteLocked
        {
            get
            {
                return RawLinkedEntity != null ? RawLinkedEntity.WriteLocked : m_WriteLocked;
            }
            set
            {
                m_WriteLocked = value;
                if (RawLinkedEntity != null)
                    RawLinkedEntity.WriteLocked = value;
            }
        }
        private PartialBoolean m_WriteLocked;

        #endregion

        #region Protected Instance Properties

        protected virtual ISmartEntity RawLinkedEntity { get; set; }

        #endregion

        #region Fields and Constants

        private readonly Dictionary<String, ISmartEffectCollection> m_FieldEffects =
            new Dictionary<String, ISmartEffectCollection>();

        private ISmartEntity m_OwningEntity;

        #endregion


        public abstract void Dispatch(ISmartDispatcher dispatcher, object context);
 

        public ISmartMetaEntity MetaEntity
        {
            get { throw new NotImplementedException(); }
        }


    }


    public class SmartEntityLink<TEntity> : SmartEntityLink, ISmartEntityLink<TEntity>
        where TEntity : class, ISmartEntity, new()
    {
        public override void Dispatch(ISmartDispatcher dispatcher, object context)
        {
            dispatcher.ProcessDispatch(this, context);
        }

        #region Constructors

        public override void SetRawLinkedEntity(ISmartEntity value)
        {
            if (LinkedEntity != null)
                UnWireEvents();
            LinkedEntity = (TEntity) value;
            if (LinkedEntity != null)
                WireEvents();
        }

        public SmartEntityLink()
        {
            /// LinkedEntity = new TEntity();
        }

        public SmartEntityLink(TEntity linkedEntity)
        {
            LinkedEntity = linkedEntity;
        }

        #endregion

        #region Public Instance Properties

        public TEntity LinkedEntity
        {
            get { return m_LinkedEntity; }
            set
            {
                if (m_LinkedEntity != null)
                    UnLinkEntity(m_LinkedEntity);
                m_LinkedEntity = value;
                if (m_LinkedEntity != null)
                    LinkEntity(m_LinkedEntity);
                InvokeLinkedtEntityChanged();
            }
        }

        #endregion

        #region Protected Instance Properties

        protected override ISmartEntity RawLinkedEntity
        {
            get { return m_LinkedEntity; }
        }

        #endregion

        #region Private Instance Methods

        private void InvokeLinkedtEntityChanged()
        {
            if (LinkedEntityChanged != null)
                LinkedEntityChanged(this, new EventArgs());
        }

        private void LinkEntity(TEntity entity)
        {
            entity.LinkedEntities.Add(this);
        }

        private void UnLinkEntity(TEntity entity)
        {
            entity.LinkedEntities.Remove(this);
        }

        #endregion

        #region Fields and Constants

        private TEntity m_LinkedEntity;

        #endregion

        #region All Other Members

        public event EventHandler LinkedEntityChanged;

        #endregion
    }
}